#include <sstream>
#include <cmath>
#include <cstring>
#include "vl_props.h"
#include "vl_prop_writer.h"
#include "vl_prop_reader.h"

using namespace VL;

namespace VL {
#define _VL_NODE_CHECK_SWITCH(block) switch(this->valueType) { \
	block \
	default: return false; \
}
#define _VL_NODE_CHECK_CASE(type1, type2, block) case type2: { \
	type1 value1 = *pickValue<type1>(this->dataPointer);\
	switch(prop.valueType) { \
		block \
		default: return false; \
	} \
	break; \
}
#define _VL_NODE_CHECK_CASE_ONLY(type1, block) case type1: { \
	switch(prop.valueType) { \
		block \
		default: return false; \
	} \
	break; \
}
#define _VL_NODE_CHECK_CASE_EQUAL_TYPE(type1, type2) case type2: {\
	type1 value2 = *pickValue<type1>(prop.dataPointer);\
	return value2 == value1; \
}
#define _VL_NODE_NUMBER_CHECK_EQUAL { \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(bool, VLPropValueType::Boolean) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(int8_t, VLPropValueType::Byte) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(uint8_t, VLPropValueType::UByte) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(int16_t, VLPropValueType::Short) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(uint16_t, VLPropValueType::UShort) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(int32_t, VLPropValueType::Int) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(uint32_t, VLPropValueType::UInt) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(int64_t, VLPropValueType::Long) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(uint64_t, VLPropValueType::ULong) \
	_VL_NODE_CHECK_CASE_EQUAL_TYPE(double, VLPropValueType::Double) \
} \


template<class T>
inline std::shared_ptr<T> pickValue(const std::shared_ptr<void>& ptr) {
	return std::static_pointer_cast<T, void>(ptr);
}

template<class T>
std::string convertIntToString(const T& data) {
	if(data == 0) {
		return "0";
	}
	std::string str;
	T loopData = data;
	T number;
	while(loopData != 0) {
		number = loopData % 10;
		if(number < 0) number = -number;
		loopData /= 10;
		str = (char)(number + '0') + str;
	}
	if(data < 0) str = '-' + str;
	return str;
}

std::string convertDoubleToString(const double& data) {
	if(data == 0) {
		return "0";
	}
	std::stringstream ss;
	ss << data;
	return ss.str();
}

constexpr const int _DOUBLE_SIZE = sizeof(double);

union double_converter {
	uint8_t buf[_DOUBLE_SIZE];
	double number;
};

}

VLProp::VLProp(const bool& value): dataPointer(std::make_shared<bool>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Boolean) {}
VLProp::VLProp(const char * value): dataPointer(std::make_shared<std::string>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::String) {}
VLProp::VLProp(const std::string& value): dataPointer(std::make_shared<std::string>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::String) {}
VLProp::VLProp(const int8_t& value): dataPointer(std::make_shared<int8_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Byte) {}
VLProp::VLProp(const uint8_t& value): dataPointer(std::make_shared<uint8_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::UByte) {}
VLProp::VLProp(const int16_t& value): dataPointer(std::make_shared<int16_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Short) {}
VLProp::VLProp(const uint16_t& value): dataPointer(std::make_shared<uint16_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::UShort) {}
VLProp::VLProp(const int32_t& value): dataPointer(std::make_shared<int32_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Int) {}
VLProp::VLProp(const uint32_t& value): dataPointer(std::make_shared<uint32_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::UInt) {}
VLProp::VLProp(const int64_t& value): dataPointer(std::make_shared<int64_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Long) {}
VLProp::VLProp(const uint64_t& value): dataPointer(std::make_shared<uint64_t>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::ULong) {}
VLProp::VLProp(const double& value): dataPointer(std::make_shared<double>(value)), extraPointer(nullptr), type(VLPropType::Node), valueType(VLPropValueType::Double) {}


const bool VLProp::boolValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}

	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (bool)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (bool)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (bool)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (bool)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (bool)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (bool)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (bool)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (bool)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (bool)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (bool)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Boolean));
	}
}

const std::string VLProp::strValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}

	switch (this->valueType) {
		case VLPropValueType::String:
			return *pickValue<std::string>(this->dataPointer);
		case VLPropValueType::Boolean:
			return pickValue<bool>(this->dataPointer) ? "true" : "false";
		case VLPropValueType::Byte:
			return convertIntToString(*pickValue<int8_t>(this->dataPointer));
		case VLPropValueType::UByte:
			return convertIntToString(*pickValue<uint8_t>(this->dataPointer));
		case VLPropValueType::Short:
			return convertIntToString(*pickValue<int16_t>(this->dataPointer));
		case VLPropValueType::UShort:
			return convertIntToString(*pickValue<uint16_t>(this->dataPointer));
		case VLPropValueType::Int:
			return convertIntToString(*pickValue<int32_t>(this->dataPointer));
		case VLPropValueType::UInt:
			return convertIntToString(*pickValue<uint32_t>(this->dataPointer));
		case VLPropValueType::Long:
			return convertIntToString(*pickValue<int64_t>(this->dataPointer));
		case VLPropValueType::ULong:
			return convertIntToString(*pickValue<uint64_t>(this->dataPointer));
		case VLPropValueType::Double:
			return convertDoubleToString(*pickValue<double>(this->dataPointer));
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::String));
	}
}

const int8_t VLProp::byteValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (int8_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (int8_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (int8_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (int8_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (int8_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (int8_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (int8_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (int8_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (int8_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (int8_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Byte));
	}
}

const uint8_t VLProp::ubyteValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (uint8_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (uint8_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (uint8_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (uint8_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (uint8_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (uint8_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (uint8_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (uint8_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (uint8_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (uint8_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::UByte));
	}
}

const int16_t VLProp::shortValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (int16_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (int16_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (int16_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (int16_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (int16_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (int16_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (int16_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (int16_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (int16_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (int16_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Short));
	}
}

const uint16_t VLProp::ushortValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (uint16_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (uint16_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (uint16_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (uint16_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (uint16_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (uint16_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (uint16_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (uint16_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (uint16_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (uint16_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::UShort));
	}
}

const int32_t VLProp::intValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (int32_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (int32_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (int32_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (int32_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (int32_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (int32_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (int32_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (int32_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (int32_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (int32_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Int));
	}
}

const uint32_t VLProp::uintValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (uint32_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (uint32_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (uint32_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (uint32_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (uint32_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (uint32_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (uint32_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (uint32_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (uint32_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (uint32_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::UInt));
	}
}

const int64_t VLProp::longValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (int64_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (int64_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (int64_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (int64_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (int64_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (int64_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (int64_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (int64_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (int64_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (int64_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Long));
	}
}

const uint64_t VLProp::ulongValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (uint64_t)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (uint64_t)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (uint64_t)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (uint64_t)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (uint64_t)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (uint64_t)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (uint64_t)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (uint64_t)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (uint64_t)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (uint64_t)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::ULong));
	}
}

const double VLProp::doubleValue() const {
	if(this->type != VLPropType::Node) {
		throw VLPropExcetion(VL_PROP_ERROR_NOT_SUPPORT, "prop is not node type");
	}
	
	switch (this->valueType) {
		case VLPropValueType::Boolean:
			return (double)*pickValue<bool>(this->dataPointer);
		case VLPropValueType::Byte:
			return (double)*pickValue<int8_t>(this->dataPointer);
		case VLPropValueType::UByte:
			return (double)*pickValue<uint8_t>(this->dataPointer);
		case VLPropValueType::Short:
			return (double)*pickValue<int16_t>(this->dataPointer);
		case VLPropValueType::UShort:
			return (double)*pickValue<uint16_t>(this->dataPointer);
		case VLPropValueType::Int:
			return (double)*pickValue<int32_t>(this->dataPointer);
		case VLPropValueType::UInt:
			return (double)*pickValue<uint32_t>(this->dataPointer);
		case VLPropValueType::Long:
			return (double)*pickValue<int64_t>(this->dataPointer);
		case VLPropValueType::ULong:
			return (double)*pickValue<uint64_t>(this->dataPointer);
		case VLPropValueType::Double:
			return (double)*pickValue<double>(this->dataPointer);
		default:
			throw VLPropExcetion(VL_PROP_ERROR_CAST, "%s cannot cast to %s", VLProp::_getValueTypeStr(this->valueType), VLProp::_getValueTypeStr(VLPropValueType::Double));
	}
}

const bool VLProp::boolValue(const bool& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->boolValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->boolValue();
		}
		throw error;
	}
}

const std::string VLProp::strValue(const std::string& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->strValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->strValue();
		}
		throw error;
	}
}

const int8_t VLProp::byteValue(const int8_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->byteValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->byteValue();
		}
		throw error;
	}
}

const uint8_t VLProp::ubyteValue(const uint8_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->ubyteValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->ubyteValue();
		}
		throw error;
	}
}

const int16_t VLProp::shortValue(const int16_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->shortValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->shortValue();
		}
		throw error;
	}
}

const uint16_t VLProp::ushortValue(const uint16_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->ushortValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->ushortValue();
		}
		throw error;
	}
}

const int32_t VLProp::intValue(const int32_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	return this->intValue();
}

const uint32_t VLProp::uintValue(const uint32_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->uintValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->uintValue();
		}
		throw error;
	}
}

const int64_t VLProp::longValue(const int64_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->longValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->longValue();
		}
		throw error;
	}
}

const uint64_t VLProp::ulongValue(const uint64_t& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->ulongValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->ulongValue();
		}
		throw error;
	}
}

const double VLProp::doubleValue(const double& defaultValue) const {
	if(this->type != VLPropType::Node) {
		return defaultValue;
	}
	try {
		return this->doubleValue();
	} catch(const VLPropExcetion& error) {
		if(error.errorCode == VL_PROP_ERROR_CAST) {
			return this->doubleValue();
		}
		throw error;
	}
}

const bool VLProp::_checkNodeEqual(const VLProp& prop) const {
	if(this->valueType == prop.valueType) {
		switch (this->valueType) {
			case VLPropValueType::String: 
				return *pickValue<std::string>(this->dataPointer) == *pickValue<std::string>(prop.dataPointer);
			case VLPropValueType::Boolean:
				return *pickValue<bool>(this->dataPointer) == *pickValue<bool>(prop.dataPointer);
			case VLPropValueType::Byte:
				return *pickValue<int8_t>(this->dataPointer) == *pickValue<int8_t>(prop.dataPointer);
			case VLPropValueType::UByte:
				return *pickValue<uint8_t>(this->dataPointer) == *pickValue<uint8_t>(prop.dataPointer);
			case VLPropValueType::Short:
				return *pickValue<int16_t>(this->dataPointer) == *pickValue<int16_t>(prop.dataPointer);
			case VLPropValueType::UShort:
				return *pickValue<uint16_t>(this->dataPointer) == *pickValue<uint16_t>(prop.dataPointer);
			case VLPropValueType::Int:
				return *pickValue<int32_t>(this->dataPointer) == *pickValue<int32_t>(prop.dataPointer);
			case VLPropValueType::UInt:
				return *pickValue<uint32_t>(this->dataPointer) == *pickValue<uint32_t>(prop.dataPointer);
			case VLPropValueType::Long:
				return *pickValue<int64_t>(this->dataPointer) == *pickValue<int64_t>(prop.dataPointer);
			case VLPropValueType::ULong:
				return *pickValue<uint64_t>(this->dataPointer) == *pickValue<uint64_t>(prop.dataPointer);
			case VLPropValueType::Double:
				return *pickValue<double>(this->dataPointer) == *pickValue<double>(prop.dataPointer);
			case VLPropValueType::Void:
				return true;
		}
	}
	if(this->valueType == VLPropValueType::String || prop.valueType == VLPropValueType::String || this->valueType == VLPropValueType::Void || prop.valueType == VLPropValueType::Void) {
		return false;
	}

	_VL_NODE_CHECK_SWITCH({
		_VL_NODE_CHECK_CASE(std::string, VLPropValueType::String, {
			_VL_NODE_CHECK_CASE_EQUAL_TYPE(std::string, VLPropValueType::String)
		})
		_VL_NODE_CHECK_CASE_ONLY(VLPropValueType::Void, {
			case VLPropValueType::Void: return true;
		})
		_VL_NODE_CHECK_CASE(bool, VLPropValueType::Boolean, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(int8_t, VLPropValueType::Byte, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(uint8_t, VLPropValueType::UByte, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(int16_t, VLPropValueType::Short, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(uint16_t, VLPropValueType::UShort, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(int32_t, VLPropValueType::Int, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(uint32_t, VLPropValueType::UInt, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(int64_t, VLPropValueType::Long, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(uint64_t, VLPropValueType::ULong, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
		_VL_NODE_CHECK_CASE(double, VLPropValueType::Double, {
			_VL_NODE_NUMBER_CHECK_EQUAL
		})
	})
}

const void VLProp::_copyNode(VLProp& targetProp) const {
	targetProp.type = this->type;
	targetProp.valueType = this->valueType;
	switch (this->valueType) {
		case VLPropValueType::String:
			targetProp.dataPointer = std::make_shared<std::string>(*pickValue<std::string>(this->dataPointer));
			break;
		case VLPropValueType::Boolean:
			targetProp.dataPointer = std::make_shared<bool>(*pickValue<bool>(this->dataPointer));
			break;
		case VLPropValueType::Byte:
			targetProp.dataPointer = std::make_shared<int8_t>(*pickValue<int8_t>(this->dataPointer));
			break;
		case VLPropValueType::UByte:
			targetProp.dataPointer = std::make_shared<uint8_t>(*pickValue<uint8_t>(this->dataPointer));
			break;
		case VLPropValueType::Short:
			targetProp.dataPointer = std::make_shared<int16_t>(*pickValue<int16_t>(this->dataPointer));
			break;
		case VLPropValueType::UShort:
			targetProp.dataPointer = std::make_shared<uint16_t>(*pickValue<uint16_t>(this->dataPointer));
			break;
		case VLPropValueType::Int:
			targetProp.dataPointer = std::make_shared<int32_t>(*pickValue<int32_t>(this->dataPointer));
			break;
		case VLPropValueType::UInt:
			targetProp.dataPointer = std::make_shared<uint32_t>(*pickValue<uint32_t>(this->dataPointer));
			break;
		case VLPropValueType::Long:
			targetProp.dataPointer = std::make_shared<int64_t>(*pickValue<int64_t>(this->dataPointer));
			break;
		case VLPropValueType::ULong:
			targetProp.dataPointer = std::make_shared<uint64_t>(*pickValue<uint64_t>(this->dataPointer));
			break;
		case VLPropValueType::Double:
			targetProp.dataPointer = std::make_shared<double>(*pickValue<double>(this->dataPointer));
			break;
		case VLPropValueType::Void:
			targetProp.dataPointer.reset();
			break;
	}
}

const void VLProp::_printNode(std::string margin) const {
	if(this->valueType == VLPropValueType::Void) {
		printf("%svoid", margin.c_str());
	} else {
		printf("%s%s", margin.c_str(), this->strValue().c_str());
	}
}

const void VLProp::_writeNodeVLC(VLPropWriter& writer) const {
	writer.write((uint8_t) this->type & 0xFF);
	writer.write((uint8_t) this->valueType & 0xFF);
	auto extra = this->extraPointer.get();
	if(extra != nullptr) {
		auto extraSize = extra->size();
		writer.write(extraSize, 2);
		for(auto iter = extra->begin() ; iter != extra->end() ; iter ++) {
			writer.writeStrWithSize(*iter);
		}
	} else {
		writer.write(0, 2);
	}
	switch (this->valueType) {
		case VLPropValueType::String: {
			auto value = *pickValue<std::string>(this->dataPointer);
			writer.writeStrWithSize(value);
			break;
		}
		case VLPropValueType::Boolean:
			writer.write(*pickValue<bool>(this->dataPointer) ? 1 : 0);
			break;
		case VLPropValueType::Byte: {
			auto value = *pickValue<int8_t>(this->dataPointer);
			writer.write(value & 0xFF);
			break;
		}
		case VLPropValueType::UByte: {
			auto value = *pickValue<uint8_t>(this->dataPointer);
			writer.write(value & 0xFF);
			break;
		}
		case VLPropValueType::Short: {
			auto value = *pickValue<int16_t>(this->dataPointer);
			writer.write(value, 2);
			break;
		}
		case VLPropValueType::UShort: {
			auto value = *pickValue<uint16_t>(this->dataPointer);
			writer.write(value, 2);
			break;
		}
		case VLPropValueType::Int: {
			auto value = *pickValue<int32_t>(this->dataPointer);
			writer.write(value, 4);
			break;
		}
		case VLPropValueType::UInt: {
			auto value = *pickValue<uint32_t>(this->dataPointer);
			writer.write(value, 4);
			break;
		}
		case VLPropValueType::Long: {
			auto value = *pickValue<int64_t>(this->dataPointer);
			writer.write(value, 8);
			break;
		}
		case VLPropValueType::ULong: {
			auto value = *pickValue<uint64_t>(this->dataPointer);
			writer.write(value, 8);
			break;
		}
		case VLPropValueType::Double: {
			auto value = *pickValue<double>(this->dataPointer);
			double_converter converter;
			converter.number = value;
			writer.write(_DOUBLE_SIZE & 0xFF);
			writer.writeBuf(converter.buf, _DOUBLE_SIZE);
			break;
		}
		case VLPropValueType::Void: {
			break;
		}
	}
}

const void VLProp::_readNodeVLC(VLPropReader& reader) {
	this->dataPointer.reset();
	this->extraPointer.reset();
	this->type = VLPropType::Node;
	this->valueType = (VLPropValueType) (reader.read() & 0xFF);
	this->_readExtraVLC(reader);
	
	switch (this->valueType) {
		case VLPropValueType::String: {
			std::string tempStr;
			reader.readStrWithSize(tempStr);
			this->dataPointer = std::make_shared<std::string>(tempStr);
			break;
		}
		case VLPropValueType::Boolean:
			this->dataPointer = std::make_shared<bool>((reader.read() & 0xFF) == 1 ? true : false);
			break;
		case VLPropValueType::Byte: {
			this->dataPointer = std::make_shared<int8_t>(reader.readNumber<int8_t>());
			break;
		}
		case VLPropValueType::UByte: {
			this->dataPointer = std::make_shared<uint8_t>(reader.readNumber<uint8_t>());
			break;
		}
		case VLPropValueType::Short: {
			this->dataPointer = std::make_shared<int16_t>(reader.readNumber<int16_t>());
			break;
		}
		case VLPropValueType::UShort: {
			this->dataPointer = std::make_shared<uint16_t>(reader.readNumber<uint16_t>());
			break;
		}
		case VLPropValueType::Int: {
			this->dataPointer = std::make_shared<int32_t>(reader.readNumber<int32_t>());
			break;
		}
		case VLPropValueType::UInt: {
			this->dataPointer = std::make_shared<uint32_t>(reader.readNumber<uint32_t>());
			break;
		}
		case VLPropValueType::Long: {
			this->dataPointer = std::make_shared<int64_t>(reader.readNumber<int64_t>());
			break;
		}
		case VLPropValueType::ULong: {
			this->dataPointer = std::make_shared<uint64_t>(reader.readNumber<uint64_t>());
			break;
		}
		case VLPropValueType::Double: {
			auto doubleSize = reader.read() & 0xFF;
			reader.readBuf(reader.buf, doubleSize);
			double_converter converter;
			::memset(converter.buf, 0, _DOUBLE_SIZE);
			auto copySize = doubleSize > _DOUBLE_SIZE ? _DOUBLE_SIZE : doubleSize;
			::memcpy(converter.buf, reader.buf, copySize);
			this->dataPointer = std::make_shared<double>(converter.number);
			break;
		}
		case VLPropValueType::Void: {
			break;
		}
	}
}

const void VLProp::_writeNodeVLP(VLPropWriter& writer, std::string margin) const {
	if(this->valueType != VLPropValueType::Void) {
		writer.writeStr("%s%s", margin.c_str(), this->strValue().c_str());
	}
}
